package com.mebs.edi.parser.dom;

import static com.mebs.edi.parser.dom.DomConverter.annotatedClasses;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.mebs.edi.model.dom.DomAttribute;
import com.mebs.edi.model.dom.DomEntity;
import com.mebs.edi.model.edi.EdiGs;
import com.mebs.edi.model.edi.EdiIsa;
import com.mebs.edi.model.edi.e211.Edi211At;
import com.mebs.edi.model.edi.e211.Edi211B2a;
import com.mebs.edi.model.edi.e211.Edi211Bol;
import com.mebs.edi.model.edi.e211.Edi211G62;
import com.mebs.edi.model.edi.e211.Edi211Gn;
import com.mebs.edi.model.edi.e211.Edi211Ms3;
import com.mebs.edi.model.edi.e211.Edi211Nn;
import com.mebs.edi.model.edi.e211.Edi211St;
import com.mebs.edi.model.edi.e214.Edi214At;
import com.mebs.edi.model.edi.e214.Edi214B10;
import com.mebs.edi.model.edi.e214.Edi214G62;
import com.mebs.edi.model.edi.e214.Edi214L11;
import com.mebs.edi.model.edi.e214.Edi214Lx;
import com.mebs.edi.model.edi.e214.Edi214N1;
import com.mebs.edi.model.edi.e214.Edi214N3;
import com.mebs.edi.model.edi.e214.Edi214N4;
import com.mebs.edi.model.edi.e214.Edi214Ng;
import com.mebs.edi.model.edi.e214.Edi214Nn;
import com.mebs.edi.model.edi.e214.Edi214St;

/**
 * 
 * This class is created so that DomToModelConverter and ModelToDomConverter
 * could share the same set of annotated classes
 * 
 * @author nit
 * @version 1.0
 * 
 */
public abstract class DomConverter {
	
	protected static Map<DomEntity, Class> annotatedClasses = new HashMap<DomEntity, Class>();
	
	static {
		addAnnotatedClass(EdiIsa.class);
		addAnnotatedClass(EdiGs.class);
		
		// 211 classes
		addAnnotatedClass(Edi211St.class);
		addAnnotatedClass(Edi211G62.class);
		addAnnotatedClass(Edi211Bol.class);
		addAnnotatedClass(Edi211B2a.class);
		addAnnotatedClass(Edi211Ms3.class);
		addAnnotatedClass(Edi211Nn.class);
		addAnnotatedClass(Edi211Gn.class);
		addAnnotatedClass(Edi211At.class);
		
		// 214 classes
		addAnnotatedClass(Edi214St.class);
		addAnnotatedClass(Edi214B10.class);
		addAnnotatedClass(Edi214L11.class);
		addAnnotatedClass(Edi214N1.class);
		addAnnotatedClass(Edi214N3.class);
		addAnnotatedClass(Edi214N4.class);
		addAnnotatedClass(Edi214G62.class);
		addAnnotatedClass(Edi214Nn.class);
		addAnnotatedClass(Edi214Ng.class);
		addAnnotatedClass(Edi214Lx.class);
		addAnnotatedClass(Edi214At.class);
	}
	
	/**
	 * 
	 * Add class to the set of annotated classes.
	 * These classes will be used for parsing DOM elements
	 * 
	 * @param annotatedClass
	 * 
	 */
	public static void addAnnotatedClass(Class annotatedClass) {
		DomEntity entityAnnotation = (DomEntity)annotatedClass.getAnnotation(DomEntity.class);
		
		if (entityAnnotation == null) {
			throw new IllegalArgumentException("Class " + annotatedClass.getName() + " does not have a DomEntity annotation");
		}

		annotatedClasses.put(entityAnnotation, annotatedClass);
	}
	
	/**
	 * Get class fields
	 * (if class's parent is also mapped as DomEntity, return its fields too)
	 * 
	 * @param model
	 * @return
	 */
	protected List<Field> getFields(Class model) {
		List<Field> fields = new ArrayList<Field>();
		fields.addAll(Arrays.asList(model.getDeclaredFields()));
		
		Class superclass = model.getSuperclass();
		if (superclass.getAnnotation(DomEntity.class) != null) {
			List<Field> superclassFields = getFields(superclass);
			fields.addAll(superclassFields);
		}	
		
		return fields;
	}
	
	/**
	 * Get XML attribute name corresponding to provided field
	 * 
	 * @param model
	 * @return
	 */
	protected String getAttributeName(Field field) {
		DomAttribute attribute = field.getAnnotation(DomAttribute.class);
		
		String attributeName = attribute.name();
		if (DomAttribute.NAME_UNSPECIFIED.equalsIgnoreCase(attributeName)) {
			attributeName = field.getName();
		}
		attributeName = attributeName.toUpperCase(); // node.getAttribute seems to be case sensitive
		
		return attributeName;
	}
}
